package air2.totc.crypt;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

// Encodage maximal : (tailleClé/8) - 11

/**
 * Encodeur RSA
 * @author tmz
 *
 */
public class RSAEncoder extends AsymetricalEncoder 
{
	private static final int keyLength = 512;
	private static final int maxEncLength = (keyLength/8) - 11;
	private static final int maxDecLength = (keyLength/8);

	@Override
	public byte[] encrypt(byte[] bOutData) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException 
	{
		Cipher c = Cipher.getInstance(Encoder.RSAString);
		// On encode avec notre clé privée
		c.init(Cipher.ENCRYPT_MODE, this.getPrvKey());
		int nbFragments = (int) Math.ceil(((double) bOutData.length)/ ((double) maxEncLength));
		
		// On découpe les fragments initiaux
		byte fragments[][] 	= Encoder.truncateArray(bOutData, maxEncLength);
		// On remplit à ras bord fragments
		/*byte[] tmp = new byte[maxEncLength];
		for(int i = 0; i < tmp.length; i++)
		{
			if(i < fragments[fragments.length - 1].length)
				tmp[i] = fragments[fragments.length - 1][i];
			else
				tmp[i] = (byte) '$';
		}
		fragments[fragments.length - 1] = tmp;*/
		byte enddata[][] 	= new byte[nbFragments][];
		byte finalData[][];
		
		// Encodage initial : notre clé privée
		for(int i = 0; i < fragments.length; i++)
		{
			enddata[i] = c.doFinal(fragments[i]);
		}
		
		// On reforme un tableau de bytes...
		bOutData = Encoder.recoverArray(enddata);
		finalData = Encoder.truncateArray(bOutData, maxEncLength);
		
		// Encodage suivant : la clé publique du destinataire
		c.init(Cipher.ENCRYPT_MODE, this.getPubKey());
		for(int i = 0; i < fragments.length; i++)
		{
			enddata[i] = c.doFinal(finalData[i]);
		}
		
		return Encoder.recoverArray(enddata);
	}

	@Override
	public byte[] decrypt(byte[] bInData) throws InvalidKeyException,
			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
		// Découpage du message
		int nbFragments = (int) Math.ceil(((double) bInData.length)/ ((double) maxDecLength));
		byte nextdata[][] = new byte[nbFragments][];
		byte finalpass[][];
		byte finaldata[][] = new byte[nbFragments][];
		byte firstPass[][] = Encoder.truncateArray(bInData, maxDecLength);
		
		// On déchiffre avec notre clé privée
		Cipher c = Cipher.getInstance(Encoder.RSAString);
		c.init(Cipher.DECRYPT_MODE, this.getPrvKey());
		for(int i = 0; i < firstPass.length; i++)
		{
			nextdata[i] = c.doFinal(firstPass[i]);
		}
		
		bInData = Encoder.recoverArray(nextdata);
		// On splitte de nouveau le tableau
		finalpass = Encoder.truncateArray(bInData, maxDecLength);
		
		// On déchiffre avec la clé publique de l'émetteur
		c.init(Cipher.DECRYPT_MODE, this.getPubKey());
		int i;
		for(i = 0; i < nextdata.length; i++)
		{
			finaldata[i] = c.doFinal(finalpass[i]); //, 0, maxEncLength);			
		}
		
		return Encoder.recoverArray(finaldata);
	}
	
	public static KeyPair genKeyPair() throws NoSuchAlgorithmException
	{
		KeyPair kp;
		KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
		kpg.initialize(RSAEncoder.keyLength);
		
		kp = kpg.generateKeyPair();
		
		return kp;
	}
}